package com.livestocksolutions.ui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.TreeMap;

import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;

import org.jfree.chart.ChartFactory;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.ChartUtilities;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.axis.DateAxis;
import org.jfree.chart.labels.StandardXYToolTipGenerator;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYDifferenceRenderer;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.time.Day;
import org.jfree.data.time.TimeSeries;
import org.jfree.data.time.TimeSeriesCollection;

import com.livestocksolutions.Bovine;
import com.livestocksolutions.Group;
import com.livestocksolutions.Mob;
import com.livestocksolutions.LivestockSolutions;
import com.livestocksolutions.Weight;
import com.livestocksolutions.util.Comparators;
import com.livestocksolutions.util.GroupList;
import com.livestocksolutions.util.JDatePickerDate;
import com.livestocksolutions.util.Table;

/**
 * Show Reports generated using the data stored in this application.
 * @author Ken DeVries
 * @author Adam Parr
 * @author Matt Lang
 * 
 * @.modified 13 March 2013
 */
public class ReportPanel extends JPanel implements TabbedInterface {
	private static final long serialVersionUID = 1L;
	
	/**
	 * Sort the member list by Visual ID tag.
	 */
	protected final int LIST_ORDER_VISUAL = 0;
	/**
	 * Sort the member list by using NLIS ID.
	 */
	protected final int LIST_ORDER_NLIS 	= 1;
	/**
	 * Sort the member list by using RF ID.
	 */
	protected final int LIST_ORDER_RF 	= 2;
	/**
	 * Sort the member list by DOB.
	 */
	protected final int LIST_ORDER_DOB 	= 3;
	/**
	 * Sort the member list by active status.
	 */
	protected final int LIST_ORDER_ACTIVE	= 4;
	/**
	 * Sort the member list by Gender.
	 */
	protected final int LIST_ORDER_GENDER = 5;

	/**
	 * The current member list order method.
	 * @see #LIST_ORDER_VISUAL
	 * @see #LIST_ORDER_NLIS
	 * @see #LIST_ORDER_RF
	 * @see #LIST_ORDER_DOB
	 * @see #LIST_ORDER_ACTIVE
	 */
	protected int orderMemberListBy = LIST_ORDER_VISUAL;
	
	/**
	 * The sort direction. Ascending : 1, Descending -1;
	 */
	protected int orderMemberHighToLow = 1;
	
	/**
	 * The Main panel
	 */
	public static final int HOME_PANEL = 1;
	/**
	 * The Weight Growth selection area.
	 */
	public static final int WEIGHT_GAIN_PANEL = 2;
	/**
	 * The Weight Growth generated report.
	 */
	public static final int WEIGHT_GAIN_PANEL_SHOW = 3;
	/**
	 * View a pedigree of a given Cattle.
	 */
	public static final int PEDIGREE_PANEL = 4;
	/**
	 * Generate a list of most productive cattle, based on a number of factors.
	 */
	public static final int PRODUCTIVE_PANEL = 5;
	/**
	 * Generate a list of most productive parent based on costs and sale prices of its offspring.
	 */
	public static final int PRODUCTIVE_PARENT_PANEL = 6;
	/**
	 * Generate a list of least productive parent based on costs and sale prices of its offspring.
	 */
	public static final int LEAST_PRODUCTIVE_PARENT = 7;
	/**
	 * Generate a list of income for a given period.
	 */
	public static final int INCOME_PANEL = 8;
	/**
	 * Generate a list of expenditure for a given period.
	 */
	public static final int EXPENDITURE_PANEL = 9;
	/**
	 * Generate a graph of expenditure and income for a given period.
	 */
	public static final int CASHFLOW_PANEL = 10;
	
	/**
	 * The Button that will be the button that will initiate the report generation .
	 */
	protected JButton doReport;
	
	protected final JScrollPane jsp = new JScrollPane();
	protected final JPanel panel = new JPanel();
	protected final JPanel bovineList = new JPanel();
	protected final JFileChooser jfc = new JFileChooser();
	
	protected ArrayList<Bovine> bList = new ArrayList<Bovine>();
	protected ArrayList<GroupList<Group>> gList = new ArrayList<GroupList<Group>>();
	
	protected JDatePickerDate dateStart = JDatePickerDate.createJDatePicker();
	protected JDatePickerDate dateEnd = JDatePickerDate.createJDatePicker();
	
	protected JFreeChart report;
	
	/**
	 * The current view.
	 * 
	 * @see #HOME_PANEL
	 * @see #WEIGHT_GAIN_PANEL
	 * @see #WEIGHT_GAIN_PANEL_SHOW
	 * @see #PEDIGREE_PANEL
	 * @see #PRODUCTIVE_PANEL
	 * @see #PRODUCTIVE_PARENT_PANEL
	 * @see #LEAST_PRODUCTIVE_PARENT
	 * @see #INCOME_PANEL
	 * @see #EXPENDITURE_PANEL
	 * @see #CASHFLOW_PANEL
	 */
	protected int viewing = -1;
	
	/**
	 * Constructor
	 */
	public ReportPanel(){
		setLayout(new BorderLayout(0, 0));
		jsp.setViewportView(panel);
		jsp.setViewportBorder(BorderFactory.createEmptyBorder());
		add(jsp);
		
		AdjustmentListener al = new AdjustmentListener(){
			@Override
			public void adjustmentValueChanged(AdjustmentEvent evt) {
				jsp.repaint();
			}
		};
		jsp.getHorizontalScrollBar().addAdjustmentListener(al);
		jsp.getVerticalScrollBar().addAdjustmentListener(al);
		
		bovineList.setLayout(new BorderLayout(0,0));
		
		dateStart.setTextEditable(true);
		dateEnd.setTextEditable(true);
	}
	
	@Override
	public void displayPanel() {
		displayPanel(viewing);
	}
	
	/**
	 * @see #HOME_PANEL
	 * @see #WEIGHT_GAIN_PANEL
	 * @see #WEIGHT_GAIN_PANEL_SHOW
	 * @see #PEDIGREE_PANEL
	 * @see #PRODUCTIVE_PANEL
	 * @see #LEAST_PRODUCTIVE_PARENT
	 * @see #INCOME_PANEL
	 * @see #EXPENDITURE_PANEL
	 * @see #CASHFLOW_PANEL
	 */
	@Override
	public void displayPanel(int panel){
		switch(panel){
			case HOME_PANEL:
				createHomePanel();
				setReportViewButton();
				break;
			case WEIGHT_GAIN_PANEL:
				setWeightGainViewButton();
				createWeightGainPanel();
				break;
			case PEDIGREE_PANEL:
				createPedigreePanel();
				setReportViewButton();
				break;
			case PRODUCTIVE_PANEL:
				createProductivePanel();
				setReportViewButton();
				break;
			case PRODUCTIVE_PARENT_PANEL:
				createProductiveParentPanel();
				setReportViewButton();
				break;
			case INCOME_PANEL:
				createIncomePanel();
				setReportViewButton();
				break;
			case EXPENDITURE_PANEL:
				createExpenditurePanel();
				setReportViewButton();
				break;
			case WEIGHT_GAIN_PANEL_SHOW:
				// Do not refresh this view, or should we?
				setGenerateWeightGainViewButton();
				break;
			default :
				createHomePanel();
				setReportViewButton();
					
		}
	}
	
	private void createHomePanel(){
		viewing = HOME_PANEL;
		
		panel.removeAll();
		final int NAME = 0;
		final int COUNT = 1;
		final int MEMBER_WEIGHTS = 2;
		final int FROM = 3;
		final int TO = 4;
		Table weightGrowth = new Table("Weight Growth Quick Reports",true,"Group/Mob","No. Members","No. Members with weights","Start","End");
		weightGrowth.setEmptyMessage("There are no Groups or Mobs that have any members with weight data.");
		weightGrowth.setHeaderClickable(NAME, false);
		weightGrowth.setHeaderClickable(COUNT, false);
		weightGrowth.setHeaderClickable(MEMBER_WEIGHTS, false);
		weightGrowth.setHeaderClickable(FROM, false);
		weightGrowth.setHeaderClickable(TO, false);
		
		for(final Group g : LivestockSolutions.getGroups()){
			boolean eligible = false;
			int no = 0;
			Date pre = null;
			Date after = null;
			for(Bovine b : g.getBovineList()) {
				Weight[] w = b.getWeighings();
				if(w.length>0) {
					eligible = true;
					no++;
					pre=pre==null||pre.after(w[0].getDate())?w[0].getDate():pre;
					after=after==null||after.before(w[w.length-1].getDate())?w[w.length-1].getDate():after;
				}
			}
			
			final Date preFinal = pre;
			final Date afterFinal = after;
			if(eligible) {
				weightGrowth.addRow(
					new JLabel(g.getName()),
					new JLabel(String.valueOf(g.getBovineList().length)),
					new JLabel(String.valueOf(no)),
					new JLabel(JDatePickerDate.format(preFinal)),
					new JLabel(JDatePickerDate.format(afterFinal)));
				weightGrowth.addMouseListener(new MouseListener(){
					@Override
					public void mouseClicked(MouseEvent e) {
						generateWeight("Weight Growth for the"+(g instanceof Mob?"Mob":"Group")+" "+g.getName(),preFinal, afterFinal, g.getBovineList());
					}
					@Override public void mousePressed(MouseEvent e) { }
					@Override public void mouseReleased(MouseEvent e) {}
					@Override public void mouseEntered(MouseEvent e) {}
					@Override public void mouseExited(MouseEvent e) {}
				});
			}
		}
		
		Table financeIncome = new Table("Income Quick Reports",true,"","");
		financeIncome.showHeaderVisible(false);
		
		JButton pfy = new JButton("Last Financial Year");
		JButton cfy = new JButton("Current Financial Year");
		
		financeIncome.addRow(pfy,cfy);
		financeIncome.setRowClickable(false);
		pfy.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				displayPanel(INCOME_PANEL);
				panel.removeAll();
				Calendar fy = Calendar.getInstance();
				fy.set(Calendar.MONTH, Calendar.JULY);
				fy.set(Calendar.DATE, 1);
				fy.add(Calendar.YEAR, -1);
				if(Calendar.getInstance().get(Calendar.MONTH)<Calendar.JULY) {
					fy.add(Calendar.YEAR, -1);
				}
				Date startDate = fy.getTime();
				
				fy.set(Calendar.MONTH, Calendar.JUNE);
				fy.set(Calendar.DATE, 31);
				fy.add(Calendar.YEAR, +1);
				Date endDate = fy.getTime();
				IncomePanel income = new IncomePanel();
				income.updatePanel(startDate, endDate);
				JPanel flow = new JPanel();
				flow.add(income);
				panel.add(flow, BorderLayout.NORTH);
				panel.validate();
				panel.repaint();
				validate();
			}
		});
		
		cfy.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				displayPanel(INCOME_PANEL);
				panel.removeAll();
				Calendar fy = Calendar.getInstance();
				fy.set(Calendar.MONTH, Calendar.JULY);
				fy.set(Calendar.DATE, 1);
				if(Calendar.getInstance().get(Calendar.MONTH)<Calendar.JULY) {
					fy.add(Calendar.YEAR, -1);
				}
				Date startDate = fy.getTime();
				fy.set(Calendar.MONTH, Calendar.JUNE);
				fy.set(Calendar.DATE, 31);
				fy.add(Calendar.YEAR, +1);
				Date endDate = fy.getTime();
				IncomePanel income = new IncomePanel();
				income.updatePanel(startDate, endDate);
				JPanel flow = new JPanel();
				flow.add(income);
				panel.add(flow, BorderLayout.NORTH);
				panel.validate();
				panel.repaint();
				validate();
			}
		});
		
		panel.setLayout(new BoxLayout(panel,BoxLayout.Y_AXIS));
		panel.add(weightGrowth);
		panel.add(financeIncome);
		panel.validate();
		panel.repaint();
		validate();
	}

	/**
	 * Setup and display the Report view buttons.
	 */
	protected synchronized void setReportViewButton(){
		if(MainFrame.getInstance().getSelectedTab()==this){
			JButton btnHome = new JButton("Report Home");
			btnHome.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(HOME_PANEL);
				}
			});
			
			JButton btnWeightGain = new JButton("Weight Growth");
			btnWeightGain.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(WEIGHT_GAIN_PANEL);
				}
			});
			
			JButton btnOffspringWeightGain = new JButton("Offspring Weight Gain");
			btnOffspringWeightGain.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(PRODUCTIVE_PARENT_PANEL);
				}
			});
			
			JButton btnPedigree = new JButton("Pedigree");
			btnPedigree.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(PEDIGREE_PANEL);
				}
			});
			
			JButton btnProductive = new JButton("Productive");
			btnProductive.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(PRODUCTIVE_PANEL);
				}
			});
			
			JButton btnIncome = new JButton("Income");
			btnIncome.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(INCOME_PANEL);
				}
			});
			
			JButton btnExpenditure = new JButton("Expenditure");
			btnExpenditure.addActionListener(new ActionListener(){
				@Override
				public void actionPerformed(ActionEvent e) {
					displayPanel(EXPENDITURE_PANEL);
					validate();
					repaint();
				}
			});
			JPanel head = new JPanel();
			head.add(new JLabel("Reports"));
			MainFrame.getInstance().clearSidePane();
			MainFrame.getInstance().addComponentTopSidePane(btnHome,new JSeparator(),head,btnWeightGain, btnOffspringWeightGain, btnPedigree, btnProductive,btnIncome,btnExpenditure);
		}
	}
	
	/**
	 * Show and create options to generate Weights for the selection of Cattle between the selected dates.
	 */
	public void createWeightGainPanel(){
		viewing = WEIGHT_GAIN_PANEL;
		panel.removeAll();
		panel.setLayout(new BorderLayout(0,0));
		
		JPanel containerPanel = new JPanel();
		JPanel titlePanel=new JPanel();
		JPanel flow = new JPanel();
		flow.add(new JLabel("Generate Weight Growth Report"));
		titlePanel.setLayout(new BorderLayout(5,5));
		titlePanel.add(flow, BorderLayout.NORTH);
		titlePanel.add(containerPanel, BorderLayout.CENTER);
		GridBagLayout gbl_containerPanel = new GridBagLayout();
		gbl_containerPanel.columnWidths = new int[] {170, 0};
		gbl_containerPanel.rowHeights = new int[]{25, 0};
		gbl_containerPanel.columnWeights = new double[]{0.0, 1.0};
		gbl_containerPanel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
		containerPanel.setLayout(gbl_containerPanel);
		
		JPanel panel_1 = new JPanel();
		GridBagConstraints gbc_panel_1 = new GridBagConstraints();
		gbc_panel_1.fill = GridBagConstraints.BOTH;
		gbc_panel_1.insets = new Insets(0, 0, 0, 10);
		gbc_panel_1.gridx = 0;
		gbc_panel_1.gridy = 0;
		containerPanel.add(panel_1, gbc_panel_1);
		panel_1.setLayout(new BorderLayout(5, 0));
		panel_1.add(new JLabel("Start Date:"), BorderLayout.WEST);
		panel_1.add(dateStart, BorderLayout.CENTER);
		
		JPanel panel_2 = new JPanel();
		GridBagConstraints gbc_panel_2 = new GridBagConstraints();
		gbc_panel_2.fill = GridBagConstraints.BOTH;
		gbc_panel_2.gridx = 1;
		gbc_panel_2.gridy = 0;
		containerPanel.add(panel_2, gbc_panel_2);
		panel_2.setLayout(new BorderLayout(5, 0));
		panel_2.add(new JLabel("End Date:"), BorderLayout.WEST);
		panel_2.add(dateEnd, BorderLayout.CENTER);
		
		// Make selection area.
		panel.add(titlePanel, BorderLayout.NORTH);
		panel.add(bovineList, BorderLayout.CENTER);
		createWeightBovineList();
		panel.validate();
		panel.repaint();
		validate();
	}
	
	/**
	 * Show the 
	 */
	protected synchronized void setWeightGainViewButton(){
		setReportViewButton();
		doReport = new JButton("Generate Report");
		doReport.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				generateWeight("", dateStart.getDate(), dateEnd.getDate(), bList.toArray(new Bovine[bList.size()]));
			}
		});
		MainFrame.getInstance().addComponentCentreSidePane(doReport);
	}
	
	/**
	 * Show a list of eligible cattle that have weighings.
	 */
	private void createWeightBovineList(){
		final JPanel bovinePanelFrame=new JPanel();
		bovinePanelFrame.setLayout(new BorderLayout(0, 0));
		final int ADD	 	= 0;
		final int VISUAL 	= 1;
		final int NLIS 		= 2;
		final int RF 		= 3;
		final int DOB 		= 4;
		final int GENDER 	= 5;
		
		final Table bovinePanel = new Table("Generate Weight Growth Chart",false,
			"Add","Visual ID","NLIS","RF ID","DOB","Gender");
		bovinePanel.setEmptyMessage("There are no cattle to select.");
		
		final JPanel btnPanel = new JPanel();
	
		new Thread(){
			@Override
			public void run(){
				bovinePanel.add(btnPanel, BorderLayout.NORTH);
				bovinePanelFrame.add(bovinePanel, BorderLayout.NORTH);
				
				bovinePanel.setHeaderClickable(ADD, false);
				bovinePanel.setHeaderOrderedBy(orderMemberListBy==LIST_ORDER_VISUAL?VISUAL:orderMemberListBy==LIST_ORDER_RF?RF:orderMemberListBy==LIST_ORDER_NLIS?NLIS:orderMemberListBy==LIST_ORDER_DOB?DOB:orderMemberListBy==LIST_ORDER_GENDER?GENDER:VISUAL, orderMemberHighToLow==1);
				bovinePanel.addHeaderMouseListener(VISUAL, new MouseListener(){
					@Override
					public void mouseClicked(MouseEvent e) {
						orderMemberHighToLow = orderMemberListBy==LIST_ORDER_VISUAL?orderMemberHighToLow*-1:orderMemberHighToLow;
						orderMemberListBy = LIST_ORDER_VISUAL;
						createWeightBovineList();
					}
					@Override public void mousePressed(MouseEvent e) {}
					@Override public void mouseReleased(MouseEvent e) {}
					@Override public void mouseEntered(MouseEvent e) {}
					@Override public void mouseExited(MouseEvent e) {}
				});
				bovinePanel.addHeaderMouseListener(RF, new MouseListener(){
					@Override
					public void mouseClicked(MouseEvent e) {
						orderMemberHighToLow = orderMemberListBy==LIST_ORDER_RF?orderMemberHighToLow*-1:orderMemberHighToLow;
						orderMemberListBy = LIST_ORDER_RF;
						createWeightBovineList();
					}
					@Override public void mousePressed(MouseEvent e) {}
					@Override public void mouseReleased(MouseEvent e) {}
					@Override public void mouseEntered(MouseEvent e) {}
					@Override public void mouseExited(MouseEvent e) {}
				});
				bovinePanel.addHeaderMouseListener(NLIS, new MouseListener(){
					@Override
					public void mouseClicked(MouseEvent e) {
						orderMemberHighToLow = orderMemberListBy==LIST_ORDER_NLIS?orderMemberHighToLow*-1:orderMemberHighToLow;
						orderMemberListBy = LIST_ORDER_NLIS;
						createWeightBovineList();
					}
					@Override public void mousePressed(MouseEvent e) {}
					@Override public void mouseReleased(MouseEvent e) {}
					@Override public void mouseEntered(MouseEvent e) {}
					@Override public void mouseExited(MouseEvent e) {}
				});
				bovinePanel.addHeaderMouseListener(DOB, new MouseListener(){
					@Override
					public void mouseClicked(MouseEvent e) {
						orderMemberHighToLow = orderMemberListBy==LIST_ORDER_DOB?orderMemberHighToLow*-1:orderMemberHighToLow;
						orderMemberListBy = LIST_ORDER_DOB;
						createWeightBovineList();
					}
					@Override public void mousePressed(MouseEvent e) {}
					@Override public void mouseReleased(MouseEvent e) {}
					@Override public void mouseEntered(MouseEvent e) {}
					@Override public void mouseExited(MouseEvent e) {}
				});
				bovinePanel.addHeaderMouseListener(GENDER, new MouseListener(){
					@Override
					public void mouseClicked(MouseEvent e) {
						orderMemberHighToLow = orderMemberListBy==LIST_ORDER_GENDER?orderMemberHighToLow*-1:orderMemberHighToLow;
						orderMemberListBy = LIST_ORDER_GENDER;
						createWeightBovineList();
					}
					@Override public void mousePressed(MouseEvent e) {}
					@Override public void mouseReleased(MouseEvent e) {}
					@Override public void mouseEntered(MouseEvent e) {}
					@Override public void mouseExited(MouseEvent e) {}
				});
	
				Group[] allgaList = LivestockSolutions.getGroups(); // Get all Groups
				Arrays.sort(allgaList,Comparators.groupComparatorName);
				
				GroupList<Group> glip = new GroupList<Group>("Select an Option");
				gList.remove(glip);
				gList.add(glip);
				
				glip = new GroupList<Group>("Add All");
				gList.remove(glip);
				gList.add(glip);
				
				glip = new GroupList<Group>("Remove All");
				gList.remove(glip);
				gList.add(glip);
				
				for(Group g : allgaList){
					glip = new GroupList<Group>(g);
					if(!gList.contains(glip)||!gList.get(gList.indexOf(glip)).isRemove()){
						// This is an add item
						gList.remove(glip);
						gList.add(glip);
					}else{
						// This is a remove item
						gList.remove(glip);
						gList.add(new GroupList<Group>(g,true));
					}
				}
				
				final JComboBox grpSelect = new JComboBox(new DefaultComboBoxModel(gList.toArray()));
				grpSelect.addActionListener(new ActionListener(){
					@Override
					public void actionPerformed(ActionEvent arg0) {
						Group g = gList.get(gList.indexOf(grpSelect.getSelectedItem())).getItem();
						if(g!=null){
							if(gList.get(gList.indexOf(grpSelect.getSelectedItem())).isRemove()){
								gList.remove(new GroupList<Group>(g));
								gList.add(new GroupList<Group>(g,false));
								for(Bovine b : g.getBovineList()){
									bList.remove(b);
								}
							}else{
								gList.remove(new GroupList<Group>(g));
								gList.add(new GroupList<Group>(g,true));
								for(Bovine b : g.getBovineList()){
									bList.add(b);
								}
							}
						}else{
							bList.clear(); // Remove All
							gList.clear(); // Remove All
							if(grpSelect.getSelectedItem().toString().equals("Add All")){
								bList.addAll(Arrays.asList(LivestockSolutions.getBovines())); // Get all Bovines
								for(Group gr :LivestockSolutions.getGroups()){
									gList.add(new GroupList<Group>(gr,true)); // Get all Groups and Mobs
								}
							}
						}
						createWeightBovineList();
					}
				});
				
				btnPanel.add(new JLabel("Group to add or remove: "));
				btnPanel.add(grpSelect);
				
				Bovine[] allbalist = LivestockSolutions.getBovines(); // Get all Bovines
				
				Arrays.sort(allbalist, orderMemberListBy==LIST_ORDER_VISUAL?Comparators.bovineComparatorVISID:orderMemberListBy==LIST_ORDER_NLIS?Comparators.bovineComparatorNLISID:orderMemberListBy==LIST_ORDER_RF?Comparators.bovineComparatorRFID:orderMemberListBy==LIST_ORDER_DOB?Comparators.bovineComparatorDOB:Comparators.bovineComparatorGender);
				ArrayList<Bovine> barrList = new ArrayList<Bovine>(Arrays.asList(allbalist));
				
				for(int x=0; x<barrList.size(); x++){
					if(barrList.get(x).getWeighings().length>0){
						final JCheckBox add = new JCheckBox();
						// Set this Animal to selected if it is in the Group filter list.
						add.setSelected(bList.contains(barrList.get(x)));
						bovinePanel.addRow(
							add,
							new JLabel(barrList.get(x).getEartag().getEartagID()),
							new JLabel(barrList.get(x).getEartag().getNlisID()),
							new JLabel(barrList.get(x).getEartag().getRfID()),
							new JLabel(barrList.get(x).getDob()!=null?JDatePickerDate.format(barrList.get(x).getDob()):"Unknown"),
							new JLabel(barrList.get(x).getGender()));
						
						final Bovine b = barrList.get(x);
						bovinePanel.addMouseListener(new MouseListener(){
							@Override
							public void mouseClicked(MouseEvent e) {
								add.setSelected(!add.isSelected());
								if(add.isSelected()){
									bList.add(b);
								}else{
									bList.remove(b);
								}
							}
							@Override public void mousePressed(MouseEvent e) { }
							@Override public void mouseReleased(MouseEvent e) {}
							@Override public void mouseEntered(MouseEvent e) {}
							@Override public void mouseExited(MouseEvent e) {}
						});
						
						bovinePanel.addDefaultMouseListenerTo(add);
						add.addMouseListener(new MouseListener(){
							@Override
							public void mouseClicked(MouseEvent e) {
								if(add.isSelected()){
									bList.add(b);
								}else{
									bList.remove(b);
								}
							}
							@Override public void mousePressed(MouseEvent e) { }
							@Override public void mouseReleased(MouseEvent e) {}
							@Override public void mouseEntered(MouseEvent e) {}
							@Override public void mouseExited(MouseEvent e) {}
						});
					}
				}
				bovineList.validate();
			}
		}.start();
		
		bovineList.removeAll();
		bovineList.add(new JLabel("<html><body><h2>Generate Weight Gain Chart</h2></body></html>"));
		bovineList.add(bovinePanelFrame,BorderLayout.CENTER);
	}
	
	/**
	 * Generate a Weight graph, for the selected cattle, limited to weighings between the given dates.
	 * @param title The Title that will appear external to the graph, but above it.
	 * @param start The Date to limit older weights, a null value means no lower limit.
	 * @param end The Date to limit newer weights, a null value means no upper limit.
	 * @param bovine The Bovines to include in this graph
	 */
	private void generateWeight(String title, Date start, Date end, Bovine... bovine){
		viewing = WEIGHT_GAIN_PANEL_SHOW;
		report = generateWeightGain(start, end, bovine);
		// Clear and add a button that will allow saving of the generated report.
		
		panel.removeAll();
		panel.add(new ChartPanel(report),BorderLayout.CENTER);
		panel.validate();
		validate();
		setGenerateWeightGainViewButton();
	}
	
	/**
	 * Show buttons for the Weight Gain report to allow saving of the generated reports.
	 */
	protected synchronized void setGenerateWeightGainViewButton(){
		setReportViewButton();
		final JButton save = new JButton("Save Report");
		MainFrame.getInstance().clearBottomSidePane();
		MainFrame.getInstance().addComponentBottomSidePane(save);
		save.addActionListener(new ActionListener(){
			@Override
			public void actionPerformed(ActionEvent arg0) {
				jfc.setDialogTitle("Choose where to save this report in PNG or JPG format");
				if(jfc.showSaveDialog(ReportPanel.this)==JFileChooser.APPROVE_OPTION){
					try {
						File f = jfc.getSelectedFile();
						if(f.getName().toLowerCase().endsWith(".jpg")){
							ChartUtilities.saveChartAsJPEG(f, report, 1024, 768);
						}else{
							if(!f.getName().toLowerCase().endsWith(".jpg")){
								f = new File(jfc.getSelectedFile().getAbsolutePath()+".png");
							}
							ChartUtilities.saveChartAsPNG(jfc.getSelectedFile(), report, 1024, 768);
						}
					}
					catch (IOException e) {
						JOptionPane.showMessageDialog(MainFrame.getInstance(),"An error occurred while trying to save the report.","Failed to save Report", JOptionPane.OK_OPTION);
					}
				}
			}
		});
	}
	
	private void createPedigreePanel(){
		viewing = PEDIGREE_PANEL;
		panel.removeAll();
		PedigreePanel pedigree = new PedigreePanel();
		JPanel flow = new JPanel();
		flow.add(pedigree);
		panel.add(flow, BorderLayout.NORTH);
		panel.validate();
		panel.repaint();
		validate();
	}
	
	/**
	 * Productive panel shows the most productive
	 */
	private void createProductivePanel(){
		viewing = PRODUCTIVE_PANEL;
		panel.removeAll();
		ProductivePanel p = new ProductivePanel();
		JPanel flow = new JPanel();
		flow.add(p);
		panel.add(flow, BorderLayout.NORTH);
		panel.validate();
		panel.repaint();
		validate();
	}
	
	/**
	 * Panel shows the most productive parent in terms of offspring growth per day
	 * ** Should probably be called offspring growth panel
	 */
	private void createProductiveParentPanel(){
		viewing = PRODUCTIVE_PARENT_PANEL;
		panel.removeAll();
		ProductiveParentPanel productiveParentPanel = new ProductiveParentPanel();
		JPanel flow = new JPanel();
		flow.add(productiveParentPanel);
		panel.add(flow, BorderLayout.NORTH);
		panel.validate();
		panel.repaint();
		validate();
	}
	
	private void createIncomePanel(){
		viewing = INCOME_PANEL;
		panel.removeAll();
		IncomePanel income = new IncomePanel();
		JPanel flow = new JPanel();
		flow.add(income);
		panel.add(flow, BorderLayout.NORTH);
		panel.validate();
		panel.repaint();
		validate();
	}
	
	private void createExpenditurePanel(){
		viewing = EXPENDITURE_PANEL;
		panel.removeAll();
		ExpenditurePanel expenditure = new ExpenditurePanel();
		JPanel flow = new JPanel();
		flow.add(expenditure);
		panel.add(flow, BorderLayout.NORTH);
		panel.validate();
		panel.repaint();
		validate();
	}	
	
	
	
	/**
	 * Generates a weight chart for the given Bovine, from birth to current date.
	 * @param bovine the Bovine(s) to generate the weight chart for.
	 * @return the generated graph.
	 */
	public static JFreeChart generateWeightGain(Bovine... bovine){
		return generateWeightGain(null, null, bovine);
	}
	
	/**
	 * Generates a weight graph of all Bovines that were born at or around the start date (within about 60 days)
	 * @param start the start date for the Graph
	 * @param end the end date for the Graph
	 * @return the generated graph
	 */
	public static JFreeChart generateWeightGain(Date start, Date end){
		ArrayList<Bovine> ba = new ArrayList<Bovine>(Arrays.asList(LivestockSolutions.getBovines()));
		Iterator<Bovine> i = ba.iterator();
		
		Calendar dateMin = Calendar.getInstance();
		Calendar dateMax = (Calendar) dateMin.clone();
		dateMin.setTime(start);
		dateMin.set(Calendar.DATE, -60);
		dateMax.set(Calendar.DATE, 60);
		while(i.hasNext()){
			Bovine b = i.next();
			if(b.getDob()!=null&&(b.getDob().before(dateMin.getTime())||b.getDob().after(dateMax.getTime()))){
				i.remove();
			}
		}
		return generateWeightGain(start, end, ba.toArray(new Bovine[]{}));
	}

	/**
	 * Generates a weight chart for the given Bovines all in one chart, from given start to end date.
	 * @param start the start date for the Graph.
	 * @param end the end date for the Graph.
	 * @param bovine the Bovines to generate the weight chart for.
	 * @return the generated graph
	 */
	public static JFreeChart generateWeightGain(Date start, Date end, Bovine... bovine){
		// Create initial Graph plot.
		TimeSeriesCollection timeDataset = new TimeSeriesCollection();
		
		TreeMap<Date,Double> average = new TreeMap<Date,Double>();
		HashMap<Date,Integer> count = new HashMap<Date,Integer>();
		
		// Loop through each animal in the list and create it's graph
		for(Bovine b : bovine){
			Weight[] weights = b.getWeighings();
			TimeSeries series = new TimeSeries(b.getEartag().getEartagID());
			for(Weight w : weights){
				if(start==null||!w.getDate().before(start)){
					if(end==null||!w.getDate().after(end)){
						series.add(new Day(w.getDate()), w.getWeight());
						if(average.get(w.getDate())==null){
							average.put(w.getDate(), w.getWeight());
							count.put(w.getDate(), 1);
						}else{
							average.put(w.getDate(), w.getWeight()+average.get(w.getDate()));
							count.put(w.getDate(), count.get(w.getDate())+1);
						}
					}else{
						break;
					}
				}
			}
			if(!series.isEmpty()){
				timeDataset.addSeries(series);
			}
		}
		TimeSeriesCollection timeDatasetAverage = new TimeSeriesCollection();
		Iterator<Date> i = average.keySet().iterator();
		TimeSeries series = new TimeSeries("Average Growth");
		while(i.hasNext()){
			Date d = i.next();
			series.add(new Day(d), average.get(d)/count.get(d));
		}
		if(!series.isEmpty()){
			timeDatasetAverage.addSeries(series);
		}
		
		JFreeChart chart = ChartFactory.createTimeSeriesChart(
			"Weight Growth Graph"+(start!=null?" From "+JDatePickerDate.format(start):"")+" To "+JDatePickerDate.format(end!=null?end:new Date()),
			"Date",
			"Weight (Kg)",
			timeDataset, true, true, true);

		Calendar startDate = Calendar.getInstance();
		if(start!=null)
			startDate.setTime(start);
		Calendar endDate = Calendar.getInstance();
		if(end!=null)
			endDate.setTime(end);
		XYPlot plot = chart.getXYPlot();
		
		XYLineAndShapeRenderer r = new XYLineAndShapeRenderer(true, true);
		r.setBaseToolTipGenerator(StandardXYToolTipGenerator.getTimeSeriesInstance());
		plot.setRenderer(0,r);
		if(timeDatasetAverage.getSeriesCount()>0){
			plot.setDataset(1, timeDatasetAverage);
			plot.setRenderer(1,new XYLineAndShapeRenderer(false, false));
			plot.setRenderer(1,new XYDifferenceRenderer(new Color(255,0,0,30), new Color(255,0,0,30), false)); 
		}
		String dateFormat = JDatePickerDate.getDateFormat();
		if(start!=null&&end!=null&&startDate.get(Calendar.YEAR)==endDate.get(Calendar.YEAR)){
			dateFormat.replaceAll("yy", "").replaceAll("yy", "");
			if(dateFormat.startsWith("/")){
				dateFormat=dateFormat.substring(1);
			}
			if(dateFormat.endsWith("/")){
				dateFormat=dateFormat.substring(0,dateFormat.length()-1);
			}
		}
		((DateAxis)plot.getDomainAxis()).setDateFormatOverride(new SimpleDateFormat(dateFormat));
		return chart;
	}

	@Override
	public long getID() {
		return viewing;
	}
}
